use std::collections::HashMap;

use serde_json::{Map, Value};
use shq_common::prelude::{
    get_page_config, report::DisplayCols, CommonPage, ExcelReader, MysqlDatabase, OrderItems, TaskType, WhereCauses
};
use shq_workflow::get_where_clause;

use crate::callback::get_callback;
#[derive(Debug, Clone)]
pub struct PageService<'a> {
    conf_name: &'a str,
    user_id: u32,
    menu_id: u32,
}
impl<'a> PageService<'a> {
    pub fn new(conf_name: &'a str, user_id: u32, menu_id: u32) -> Self {
        PageService {
            conf_name,
            user_id,
            menu_id,
        }
    }
    pub fn validate_regex_rule(
        &self,
        row_data: &Map<String, Value>,
    ) -> anyhow::Result<HashMap<String, String>> {
        let conf = get_page_config(self.conf_name)?;
        let mut errors: HashMap<String, String> = HashMap::new();
        let leafs = conf.get_leaf_columns();
        for f in leafs.iter() {
            if let Some(edit_props) = &f.edit_props {
                if let Some(rule) = &edit_props.regex_rule {
                    let name = f.name.as_ref().unwrap();
                    if let Some(value) = row_data.get(name) {
                        if let Some(v) = value.as_str() {
                            let res = shq_common::prelude::parse_regex(rule, v);
                            if let Err(_e) = res {
                                errors.insert(
                                    f.name.clone().unwrap_or_default(),
                                    format!("字段:{name}值:{v}不符合正则规则: {}", rule.as_str()),
                                );
                            }
                        }
                    }
                }
            }
        }
        Ok(errors)
    }
    pub async fn get_config(&self) -> anyhow::Result<CommonPage> {
        let conf = get_page_config(self.conf_name).map_err(|e| anyhow::anyhow!(e.to_string()))?;
        let mut user_conf = conf.clone();
        let mut del_idxs: Vec<usize> = vec![];
        for (i, f) in user_conf.field_props.iter_mut().enumerate() {
            if let Some(ep) = f.edit_props.as_mut() {
                if ep.init_val.is_some() {
                    let dv = ep.init_val.as_ref().unwrap().get_value().await?;
                    ep.init_val = Some(shq_common::prelude::DefaultValue::Str(dv));
                }
            }
            if f.popedom_types.is_empty() {
                continue;
            }
            for pt in f.popedom_types.iter() {
                let has = shq_auth::has_column_range(self.user_id, self.menu_id, pt).await?;
                if !has {
                    del_idxs.push(i);
                }
            }
        }
        for i in del_idxs.iter().rev() {
            user_conf.field_props.remove(*i);
        }
        Ok(user_conf)
    }
    pub async fn get_list_data(
        &self,
        filters: Option<WhereCauses>,
        orders: Option<OrderItems>,
        display_cols: DisplayCols,
        page: u64,
        page_size: u64,
        flow_task_type: Option<TaskType>
    ) -> anyhow::Result<(Vec<serde_json::Value>, u64)> {
        let tx = shq_common::prelude::get_my_pool().begin().await?;
        let conf = get_page_config(self.conf_name).map_err(|e| anyhow::anyhow!(e.to_string()))?;
        let range = shq_auth::get_row_range(self.user_id, self.menu_id).await?;
        let auth_filter = conf
            .auth_filter
            .as_ref()
            .map(|af| af.to_where_cause(range, self.user_id));
        let mut table_name:Option<String>=None;
        let extra_filter = if let Some(task_type) = flow_task_type {
            if task_type == TaskType::Completed {
                table_name=Some(format!("{}_his", self.conf_name));
            }
            if let Some(af)=auth_filter {
                Some(format!("{} and {}", af, get_where_clause(self.user_id, task_type)))
            } else {
                Some(get_where_clause(self.user_id, task_type))
            }
        } else {
            auth_filter
        };
        let cb = get_callback(self.conf_name);
        let mut mydb = MysqlDatabase::from_with_tx(self.conf_name, cb, tx)?;
        let res = mydb
            .find_in_page(
                table_name,
                filters,
                extra_filter.as_deref(),
                orders,
                &display_cols,
                page,
                page_size,
            )
            .await?;
        mydb.commit().await?;
        Ok(res)
    }
    pub async fn save_row_data(&self, row_data: Map<String, Value>) -> anyhow::Result<u64> {
        let tx = shq_common::prelude::get_my_pool().begin().await?;
        let cb = get_callback(self.conf_name);
        let mut mydb = MysqlDatabase::from_with_tx(self.conf_name, cb, tx)?;
        let res = mydb.save_by_json(row_data).await?;
        mydb.commit().await?;
        Ok(res)
    }
    pub async fn update_table_rows(
        &self,
        row_datas: Vec<Map<String, Value>>,
    ) -> anyhow::Result<Vec<(usize, String)>> {
        let tx = shq_common::prelude::get_my_pool().begin().await?;
        let cb = get_callback(self.conf_name);
        let mut mydb = MysqlDatabase::from_with_tx(self.conf_name, cb, tx)?;
        let mut count = 0u64;
        let mut errors: Vec<(usize, String)> = vec![];
        for (i, row_data) in row_datas.into_iter().enumerate() {
            let res = mydb.update_part_by_json(row_data).await;
            if let Err(e) = res {
                let msg = e.to_string();
                if msg.starts_with("callback:") {
                    let msg = msg[9..].to_string();
                    errors.push((i, msg));
                } else {
                    return Err(anyhow::anyhow!(
                        "更新第{}行数据失败: {}",
                        i + 1,
                        e.to_string()
                    ));
                }
            }
            count = count + 1;
        }
        mydb.commit()
            .await
            .or_else(|e| Err(anyhow::anyhow!("提交事务失败:{e}")))?;
        Ok(errors)
    }

    pub async fn delete_by_ids(&self, ids: &[u64]) -> anyhow::Result<u64> {
        let tx = shq_common::prelude::get_my_pool().begin().await?;
        let cb = get_callback(self.conf_name);
        let mut mydb = MysqlDatabase::from_with_tx(self.conf_name, cb, tx)?;
        let res = mydb.delete_by_ids(ids).await?;
        mydb.commit().await?;
        Ok(res)
    }
    pub async fn import_excel(
        &self,
        paras_map: HashMap<String, String>,
        excel_reader: &ExcelReader,
    ) -> anyhow::Result<u64> {
        let cb = get_callback(self.conf_name);
        let res =
            shq_common::prelude::import_by_excel(self.conf_name, paras_map, excel_reader, cb)
                .await?;
        Ok(res)
    }
    pub async fn export_excel(
        &self,
        display_cols: DisplayCols,
        filters: Option<WhereCauses>,
        orders: Option<OrderItems>,
    ) -> anyhow::Result<Vec<u8>> {
        let cb = get_callback(self.conf_name);
        let conf = get_page_config(self.conf_name).map_err(|e| anyhow::anyhow!(e.to_string()))?;
        let range = shq_auth::get_row_range(self.user_id, self.menu_id).await?;
        let auth_filter = conf
            .auth_filter
            .as_ref()
            .map(|af| af.to_where_cause(range, self.user_id));
        let res = shq_common::prelude::export_xlsx(
            self.conf_name,
            display_cols,
            filters,
            auth_filter.as_deref(),
            orders,
            cb,
        )
        .await?;
        Ok(res)
    }
    pub async fn get_search_options(
        &self,
        col_name: &str,
        value: &str,
    ) -> anyhow::Result<Vec<(String, String)>> {
        let conf = get_page_config(self.conf_name)?;
        let col = conf
            .field_props
            .iter()
            .find(|f| f.name.as_deref() == Some(col_name))
            .ok_or_else(|| {
                anyhow::anyhow!("在页面{}配置中没有找到{}字段", self.conf_name, col_name)
            })?;
        let ep = col.edit_props.as_ref().ok_or_else(|| {
            anyhow::anyhow!(
                "在页面{}配置中{}字段没有配置编辑属性",
                self.conf_name,
                col_name
            )
        })?;
        let opts = ep.options.as_ref().ok_or_else(|| {
            anyhow::anyhow!(
                "在页面{}配置中{}字段没有配置选项options",
                self.conf_name,
                col_name
            )
        })?;
        let vals = opts
            .search_values(value)
            .await
            .map_err(|e| anyhow::anyhow!("获取{}字段的搜索选项失败:{}", col_name, e))?;
        Ok(vals)
    }
}
